#include "libcomm.h"

ThreadNullLock::~ThreadNullLock(void){}
void ThreadNullLock::Lock(){}
void ThreadNullLock::UnLock(){}

ThreadLock::ThreadLock(void)
{
#ifdef COMM_MSVC
	::InitializeCriticalSection(&m_cs);
#else
	pthread_mutexattr_t attr;
	MYLIB_ASSERT_VALUE(0,pthread_mutexattr_init(&attr));
	MYLIB_ASSERT_VALUE(0,pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_PRIVATE));
#ifdef __CYGWIN__
	MYLIB_ASSERT_VALUE(0,pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE));
#else
	MYLIB_ASSERT_VALUE(0,pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE_NP));
#endif
	MYLIB_ASSERT_VALUE(0,pthread_mutex_init(&m_mutex, &attr));
	MYLIB_ASSERT_VALUE(0,pthread_mutexattr_destroy(&attr));
#endif
}

ThreadLock::~ThreadLock(void)
{
#ifdef COMM_MSVC
	::DeleteCriticalSection(&m_cs);
#else
	MYLIB_ASSERT_VALUE(0,pthread_mutex_destroy(&m_mutex));
#endif
}

void ThreadLock::Lock()
{
#ifdef COMM_MSVC
	::EnterCriticalSection(&m_cs); 
#else
	// These functions shall not return an error code of [EINTR].
	MYLIB_ASSERT_VALUE(0,pthread_mutex_lock(&m_mutex));
#endif
}

void ThreadLock::UnLock()
{
#ifdef COMM_MSVC
	::LeaveCriticalSection(&m_cs);
#else
	MYLIB_ASSERT_VALUE(0,pthread_mutex_unlock(&m_mutex));
#endif
}

ThreadLockGuard::ThreadLockGuard(ThreadNullLock* lock)
	: m_lock(lock)
{
	if(NULL != m_lock)
		m_lock->Lock();
}

ThreadLockGuard::~ThreadLockGuard(void)
{
	if (NULL != m_lock)
		m_lock->UnLock();
}


ThreadFreeLock::ThreadFreeLock(void):m_threadid(0), m_lockcount(0)
{
}

void ThreadFreeLock::Lock()
{
	int threadid = Thread::Id();
	MYLIB_ASSERT(0!=threadid);
	do 
	{
		int threadid_last = Atom::CompareExchange(m_threadid, threadid, 0);
		if (0 == threadid_last)
		{
			// enter.
			m_lockcount = 1;
			break; 
		}
		if (threadid == threadid_last)
		{
			// re-enter.
			++m_lockcount;
			break;
		}
		Thread::Sleep(0);
	} while (true);
	
}
void ThreadFreeLock::UnLock()
{
	int threadid = Thread::Id();
	MYLIB_ASSERT(0!=threadid);
	//MYLIB_MEMORY_FENCE;
	if (threadid == Atom::CompareExchange(m_threadid, threadid, threadid))
	{
		// the thread had in control yet.
		--m_lockcount;
		MYLIB_ASSERT(m_lockcount >= 0);
		if (0 == m_lockcount)
		{
			// leave.
			Atom::Exchange(m_threadid, 0);
		}
		return;
	}

	MYLIB_ASSERT(!"current thread have not been locked.");
}
